﻿using System;

/*
 * 创建人：
 * 
 * 时间：
 * 
 * 说明：
 * 
 * 
 * 修改说明：
 * 
 * 
 */


namespace ConsoleTest.ChapterFive
{
    /// <summary>
    /// 线程与资源亲缘性
    /// </summary>
    public class Affinity
    {
        /*思想
         * 线程之间共享数据，因此需要对数据访问进行同步。
         * 使用线程亲缘性的思想在于：使资源属于某个单独的线程，总使用一个线程去访问资源
         * 该资源不被共享，这样就不需要进行并发访问控制了。
         */

        /*System.ThreadStatic 属性
         * 
         * 用 ThreadStaticAttribute 标记的 static 字段不在线程之间共享。
         * 每个执行线程都有单独的字段实例，并且独立地设置及获取该字段的值。
         * 如果在不同的线程中访问该字段，则该字段将包含不同的值。
         */

        /*初始化注意事项
         * 最好避免在声明时进行初始化，因为这样只有载入该类的线程会完成它
         * 自己版本的初始化。
         */


        public static void ThreadStatic()
        {
            DisplayStr();
            System.Threading.Thread t1 = new System.Threading.Thread(ChangeStr);
            t1.Start();
            t1.Join();
            DisplayStr();
        }

        //指示静态字段的值对于每个线程都是唯一的
        [System.ThreadStatic]
        private static string str = "Initilize value!";

        private static void ChangeStr()
        {
            DisplayStr();
            str = "changed Value!";
            DisplayStr();
        }
        private static void DisplayStr()
        {
            System.Console.WriteLine("Thread#{0} str={1}"
                ,System.Threading.Thread.CurrentThread.ManagedThreadId
                ,str);
        }


        /*线程本地存储（TLS Thread Local Store）与数据槽DataSlot
         * 
         * 其实质：对内存的一种封装，封装后的数据存储区域属于单个线程；
         * 
         * NET Framework 提供了两种使用托管 TLS 的方式：线程相关的静态字段和数据槽。
         * 
         * 如果您可以在编译时预料到您的确切需要，请使用线程相关的静态字段（在 Visual Basic 中为线程相关的 Shared 字段）。
         * 线程相关的静态字段可提供最佳性能。它们还具备编译时类型检查的优点。
            如果只能在运行时发现您的实际需要，请使用数据槽。
         * 数据槽比线程相关的静态字段慢一些且更加难于使用，并且数据存储为 Object 类型，因此必须将其强制转换为正确的类型才能使用。
         * 
         * 无论是使用线程相关的静态字段还是使用数据槽，托管 TLS 中的数据都是线程和应用程序域组合所独有的。
         * 在应用程序域内部，一个线程不能修改另一个线程中的数据，即使这两个线程使用同一个字段或槽时也不能。
         * 当线程从多个应用程序域中访问同一个字段或槽时，会在每个应用程序域中维护一个单独的值。
         * 
         * GC不会销毁有名数据槽，这个工作由程序员自己负责。
         */


        #region DataSlot
        private static readonly int NTHREAD = 3; //创建线程数目
        private static readonly int MaxCall = 2;//调用fServer方法次数
        private static readonly int PERIOD = 1000;//调用周期


        /// <summary>
        /// 有名数据槽测试
        /// </summary>
        public static void NamedDataSlot()
        {
            System.Console.WriteLine("Main Thread#{0} ,Hello"
                ,System.Threading.Thread.CurrentThread.ManagedThreadId);

            System.Threading.Thread.AllocateNamedDataSlot("counter");
            System.Threading.Thread thread;
            for (int i = 0; i < NTHREAD; i++)
            {
                thread = new System.Threading.Thread(NamedThreadProc);
                thread.Start();
                
            }

            System.Threading.Thread.Sleep(PERIOD * (MaxCall + 1));
            System.Console.WriteLine("Main Thread#{0} Bye!"
                ,System.Threading.Thread.CurrentThread.ManagedThreadId);
        }

        /// <summary>
        /// 设置数据槽中的数据
        /// </summary>
        private static bool NamedFServer()
        {
            //封装内存槽以保存数据
            //如果指定名称的数据槽不存在则创建新的数据槽，如果存在则返回局部数据槽
            System.LocalDataStoreSlot dSlot = System.Threading.Thread.GetNamedDataSlot("counter");

            int counter = (int)System.Threading.Thread.GetData(dSlot);

            counter++;
            System.Threading.Thread.SetData(dSlot, counter);
            
            return !(counter == MaxCall);
        }

        /// <summary>
        /// 读取数据槽中的数据
        /// </summary>
        private static void NamedThreadProc()
        {
            System.LocalDataStoreSlot dSlot = System.Threading.Thread.GetNamedDataSlot("counter");
            System.Threading.Thread.SetData(dSlot, (int)0);

            do
            {
                System.Threading.Thread.Sleep(PERIOD);
                System.Console.WriteLine("Thread#{0} I've called fServer, counter={1}"
                    ,System.Threading.Thread.CurrentThread.ManagedThreadId
                    ,(int)System.Threading.Thread.GetData(dSlot));
            }
            while (NamedFServer());

            System.Console.WriteLine("Thread#{0} bye"
                ,System.Threading.Thread.CurrentThread.ManagedThreadId);
        }


        private static  System.LocalDataStoreSlot dataSlot;

        /// <summary>
        /// 无名数据槽测试
        /// </summary>
        public static void UnNamedDataSlot()
        {
            System.Console.WriteLine("Main Thread#{0} ,Hello"
                , System.Threading.Thread.CurrentThread.ManagedThreadId);

            dataSlot = System.Threading.Thread.AllocateDataSlot();
            System.Threading.Thread thread;
            for (int i = 0; i < NTHREAD; i++)
            {
                thread = new System.Threading.Thread(UnNamedThreadProc);
                thread.Start();

            }

            System.Threading.Thread.Sleep(PERIOD * (MaxCall + 1));
            System.Console.WriteLine("Main Thread#{0} Bye!"
                , System.Threading.Thread.CurrentThread.ManagedThreadId);
        }

        /// <summary>
        /// 设置数据槽中的数据
        /// </summary>
        private static bool UnNamedFServer()
        {

            int counter = (int)System.Threading.Thread.GetData(dataSlot);

            counter++;
            System.Threading.Thread.SetData(dataSlot, counter);

            return !(counter == MaxCall);
        }

        /// <summary>
        /// 读取数据槽中的数据
        /// </summary>
        private static void UnNamedThreadProc()
        {
           
            System.Threading.Thread.SetData(dataSlot, (int)0);

            do
            {
                System.Threading.Thread.Sleep(PERIOD);
                System.Console.WriteLine("Thread#{0} I've called fServer, counter={1}"
                    , System.Threading.Thread.CurrentThread.ManagedThreadId
                    , (int)System.Threading.Thread.GetData(dataSlot));
            }
            while (UnNamedFServer());

            System.Console.WriteLine("Thread#{0} bye"
                , System.Threading.Thread.CurrentThread.ManagedThreadId);
        }
        #endregion

    }
 
}